import { ApolloClient, createHttpLink } from "@apollo/client/core";
import { getMainDefinition } from "@apollo/client/utilities";
import { split } from "@apollo/client/core";
import { WebSocketLink } from "@apollo/link-ws";
import { onError } from "@apollo/client/link/error";

import { ApolloLink, concat, from } from "apollo-link";

import { ApolloClients } from "@vue/apollo-composable";

import { boot } from "quasar/wrappers";
import { LocalStorage } from "quasar";
import { SessionStorage } from "quasar";

import fetch from "cross-fetch";

import { getClientOptions } from "src/apollo";

import { getAPIurl } from "../utils";

let apollo = null;
let wsLink = null;
const urlFull = new URL(getAPIurl());
const url = `${urlFull.host}${urlFull.pathname}`;
const urlProtocol = urlFull.protocol;

const wsProtocol =
  urlProtocol === "http:"
    ? "ws://"
    : urlProtocol === "https:"
    ? "wss://"
    : null;

export default boot(({ app, ssrContext, store }) => {
  const headers = (addJwt = false) => {
    const obj = {
      "X-CSRFTOKEN":
        SessionStorage.getItem("csrf_token") ||
        LocalStorage.getItem("csrf_token") ||
        null,
      "X-AUTH-TOKEN":
        SessionStorage.getItem("auth_token") ||
        LocalStorage.getItem("auth_token") ||
        null,
    };
    const jwt =
      SessionStorage.getItem("jwt_token") ||
      LocalStorage.getItem("jwt_token") ||
      null;
    if (jwt != null && addJwt) {
      obj["Authorization"] = `JWT ${jwt}`;
    }
    return obj;
  };

  const options = getClientOptions(/* {app, router ...} */);

  // ****** клиент default ******
  const middlewareLink = new ApolloLink((operation, forward) => {
    operation.setContext({
      headers: headers(true),
    });
    return forward(operation);
  });

  const httpLink = createHttpLink({
    uri: `${urlProtocol}//${url}jwt/`,
    fetch: fetch,
    fetchOptions: {
      credentials: "include",
      cache: "no-cache",
    },
  });
  const errorLink = onError(({ graphQLErrors, networkError }) => {
    if (graphQLErrors)
      graphQLErrors.forEach(({ message, locations, path }) =>
        console.log(
          `[GraphQL error]: Message: ${message}, Location: ${locations}, Path: ${path}`
        )
      );

    if (networkError) console.log(`[Network error]: ${networkError}`);
  });

  wsLink = process.env.CLIENT
    ? new WebSocketLink({
        uri: `${wsProtocol}${url}ws/`,
        webSocketProtocols: "graphql-ws",
        options: {
          reconnect: true,
          // reconnectionAttempts: 10,
          // inactivityTimeout: 10 * 1000,
          lazy: true,
          connectionParams: () => {
            const jwt =
              LocalStorage.getItem("jwtToken") === "null"
                ? null
                : LocalStorage.getItem("jwtToken");
            return jwt
              ? {
                  authorization: `JWT ${jwt}`,
                }
              : {};
          },
          connectionCallback: async (errors, result) => {
            // console.log('Result: ', result);
            if (errors) {
              console.log("Errors: ", errors);
            }
          },
        },
      })
    : null;

  const splitLink = process.env.CLIENT
    ? split(
        ({ query }) => {
          const definition = getMainDefinition(query);
          return (
            definition.kind === "OperationDefinition" &&
            definition.operation === "subscription"
          );
        },
        wsLink,
        httpLink
      )
    : httpLink;
  const clientDefault = new ApolloClient({
    ...options,
    link: concat(middlewareLink, from([errorLink, splitLink])),
  });

  clientDefault.onResetStore(async () => {
    // console.log('onreset');
    clientDefault.setLink(concat(middlewareLink, splitLink));
  });
  // ****** клиент default ******

  // ****** клиент registrado ******
  const middlewareLinkRegistrado = new ApolloLink((operation, forward) => {
    operation.setContext({
      headers: headers(),
    });
    return forward(operation);
  });

  const registradoLink = createHttpLink({
    uri: `${urlProtocol}//${url}registrado/`,
    fetch: fetch,
    fetchOptions: {
      credentials: "include",
    },
  });
  const registradoErrorLink = onError(({ graphQLErrors, networkError }) => {
    if (graphQLErrors)
      graphQLErrors.forEach(({ message, locations, path }) =>
        console.log(
          `[GraphQL error]: Message: ${message}, Location: ${locations}, Path: ${path}`
        )
      );

    if (networkError) console.log(`[Network error]: ${networkError}`);
  });

  const clientRegistrado = new ApolloClient({
    ...options,
    link: concat(
      middlewareLinkRegistrado,
      from([registradoErrorLink, registradoLink])
    ),
  });
  // ****** клиент registrado ******

  const apolloClients = {
    default: clientDefault,
    registrado: clientRegistrado,
  };
  apollo = apolloClients;
  app.provide(ApolloClients, apolloClients);
});
export { apollo, wsLink };
